package indigo.ueditor.config;

import static java.util.Objects.requireNonNull;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;

import indigo.core.DynamicRequestMappingHandlerMapping;
import indigo.ueditor.JsonConfigReader;
import indigo.ueditor.UEditorController;
import indigo.ueditor.UEditorFileManager;
import indigo.ueditor.UEditorHandler;
import indigo.ueditor.handler.Base64UploadHandler;
import indigo.ueditor.handler.ConfigLoaderHandler;
import indigo.ueditor.handler.MultipartUploadHandler;
import indigo.ueditor.handler.RemoteFetchHandler;
import indigo.ueditor.handler.ResourceListHandler;

@Configuration
public class UEditorConfiguration extends WebMvcConfigurerAdapter implements ImportAware, ResourceLoaderAware {
	public static final String INTERNAL_DEFAULT_CONFIG_LOCATION = "classpath:META-INF/ueditor/ueditor.json";

	public static final String DEFAULT_PATH = "/ueditor";
	public static final String DEFAULT_CONFIG_LOCATION = "classpath:ueditor.json";

	private String path;
	private String configLocation;
	private String repository;

	private ResourceLoader resourceLoader;

	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		AnnotationAttributes attrs = AnnotationAttributes.fromMap(
				importMetadata.getAnnotationAttributes(EnableUEditor.class.getName()));
		path = attrs.getString("path");
		configLocation = attrs.getString("configLocation");
		repository = requireNonNull(Strings.emptyToNull(attrs.getString("repository")),
				"repository cannot be null");
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	private JsonConfigReader getJsonConfig() throws IOException {
		Resource configResource = resourceLoader.getResource(configLocation);
		if (!configResource.exists()) {
			configResource = resourceLoader.getResource(INTERNAL_DEFAULT_CONFIG_LOCATION);
		}
		try (InputStream stream = configResource.getInputStream()) {
			return new JsonConfigReader(stream);
		}
	}

	@Bean
	public ResourceListHandler ueditorListFileHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new ResourceListHandler("listfile", fileManager,
				config.getInt("fileManagerListSize", 20),
				config.getString("fileManagerListPath", ""),
				config.getStringSet("fileManagerAllowFiles"));
	}

	@Bean
	public ResourceListHandler ueditorListImageHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new ResourceListHandler("listimage", fileManager,
				config.getInt("imageManagerListSize", 20),
				config.getString("imageManagerListPath", ""),
				config.getStringSet("imageManagerAllowFiles"));
	}

	@Bean
	public MultipartUploadHandler ueditorUploadFileHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new MultipartUploadHandler("uploadfile", fileManager,
				config.getString("filePathFormat", ""),
				config.getLong("fileMaxSize", -1L),
				config.getStringSet("fileAllowFiles"));
	}

	@Bean
	public MultipartUploadHandler ueditorUploadImageHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new MultipartUploadHandler("uploadimage", fileManager,
				config.getString("imagePathFormat", ""),
				config.getLong("imageMaxSize", -1L),
				config.getStringSet("imageAllowFiles"));
	}

	@Bean
	public MultipartUploadHandler ueditorUploadVideoHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new MultipartUploadHandler("uploadvideo", fileManager,
				config.getString("videoPathFormat", ""),
				config.getLong("videoMaxSize", -1L),
				config.getStringSet("videoAllowFiles"));
	}

	@Bean
	public Base64UploadHandler ueditorUploadScrawlHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new Base64UploadHandler("uploadscrawl", fileManager,
				config.getString("scrawlFieldName", ""),
				config.getString("scrawlPathFormat", ""),
				"scrawl.jpg",
				config.getLong("scrawlMaxSize", -1L));
	}

	@Bean
	public RemoteFetchHandler ueditorCatchImageHandler(UEditorFileManager fileManager) throws IOException {
		JsonConfigReader config = getJsonConfig();
		Map<String, String> mappings = Maps.newHashMap();
		mappings.put("image/gif", ".gif");
		mappings.put("image/jpeg", ".jpg");
		mappings.put("image/jpg", ".jpg");
		mappings.put("image/png", ".png");
		mappings.put("image/bmp", ".bmp");
		return new RemoteFetchHandler("catchimage", fileManager,
				config.getString("catcherFieldName", "") + "[]",
				config.getString("catcherPathFormat", ""),
				"remote",
				config.getLong("catcherMaxSize", -1L),
				config.getStringSet("catcherAllowFiles"),
				config.getStringSet("catcherLocalDomain"),
				mappings);
	}

	@Bean
	public ConfigLoaderHandler ueditorConfigHandler() throws IOException {
		JsonConfigReader config = getJsonConfig();
		return new ConfigLoaderHandler("config", config.getAll());
	}

	@Bean
	public UEditorFileManager ueditorFileManager() {
		return new UEditorFileManager(repository);
	}

	@Bean
	public UEditorController ueditorController(List<UEditorHandler> handlers) {
		return new UEditorController(handlers);
	}

	@Bean
	public HandlerMapping ueditorHandlerMapping(UEditorController ueditorController) {
		DynamicRequestMappingHandlerMapping handlerMapping = new DynamicRequestMappingHandlerMapping();
		handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
		handlerMapping.setHandler(ueditorController);
		handlerMapping.setPrefix(path);
		return handlerMapping;
	}

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler(path + "/**")
				.addResourceLocations("classpath:META-INF/ueditor/static/");
		registry.addResourceHandler(path + "/upload/**")
				.addResourceLocations(new File(repository).toPath().toUri().toString());
	}
}